/**Filename: blockingQueue.c
 * Author(s): Vladimir Dudnic, Michael Groenier, Derek Fleener
 * Winter 2013 TCSS422
 * Project 2
 */

#include"scheduler.h"
#include"time.h"
#include"stdlib.h"
#include"stdio.h"

void printList(arrayList*);

Process* nextRoundRobinProcess(Scheduler* the_scheduler){
  return blockingQueue_dequeue(the_scheduler->readyQueue);
}
Process* nextPriorityProcess(Scheduler* the_scheduler){
  Process* item;
  blockingQueue* temp_q;
  int i = 0;
  for(i = PRIORITY_VERY_HIGH; i >= PRIORITY_VERY_LOW;i--){
    temp_q = the_scheduler->readyPriorityQueue[i];
    if(temp_q->q->backingStore->size > 0){
      item = blockingQueue_dequeue(temp_q);
      break;
    }
  }
  return item;
}

Process* nextLotteryProcess(Scheduler* the_scheduler){
  blockedList tickets;
  int i,j = 0;
  Process* temp;
  printf("\nArray before nextPRoc.");
  printList(the_scheduler->readyArrayQueue->al);

  pthread_mutex_lock(the_scheduler->lock);
  blockedList_init(&tickets, 1);

  for(i=0;i<the_scheduler->readyArrayQueue->al->size;i++){
    temp = (Process*)blockedList_get(the_scheduler->readyArrayQueue, i);
    for(j = 0 ;j < temp->priority + 1; j++){
      blockedList_add(&tickets, i);
    }
  }

  srand(time(NULL));

  i = rand() % tickets.al->size;
  i = blockedList_get(&tickets, i);


  temp = blockedList_remove(the_scheduler->readyArrayQueue, i);

  pthread_mutex_unlock(the_scheduler->lock);

  blockedList_destroy(&tickets);

  printList(the_scheduler->readyArrayQueue->al);
  printf("\nArray after nextProc.");

  return temp;
}

void readyRoundRobinProcess(Scheduler* the_scheduler, Process* the_proc){
  blockingQueue_enqueue(the_scheduler->readyQueue, the_proc);
}
void readyPriorityProcess(Scheduler* the_scheduler, Process* the_proc){
  blockingQueue_enqueue(the_scheduler->readyPriorityQueue[the_proc->priority], the_proc);
}
void readyLotteryProcess(Scheduler* the_scheduler, Process* the_proc){
  printf("\nArray before readying.");
  printList(the_scheduler->readyArrayQueue->al);
  pthread_mutex_lock(the_scheduler->lock);
  blockedList_add(the_scheduler->readyArrayQueue, the_proc);
  pthread_mutex_unlock(the_scheduler->lock);
  printList(the_scheduler->readyArrayQueue->al);
  printf("\nArray after readying.");
}

int schedulerInit(Scheduler* the_scheduler, SchedulerType the_type){
  int rv,i = 0;

  the_scheduler->type = the_type;
  the_scheduler->lock = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
  pthread_mutex_init(the_scheduler->lock, NULL);

  switch(the_type){
  case ROUND_ROBIN:
    the_scheduler->readyQueue = (blockingQueue*)malloc(sizeof(blockingQueue));
    blockingQueue_init(the_scheduler->readyQueue);
    the_scheduler->nextProcess = &nextRoundRobinProcess;
    the_scheduler->readyProcess = &readyRoundRobinProcess;
    break;
  case PRIORITY:
    the_scheduler->readyPriorityQueue = (blockingQueue**)malloc(sizeof(blockingQueue*) * 5);
    for(i = 0; i < 5; i++){
      the_scheduler->readyPriorityQueue[i] = (blockingQueue*)malloc(sizeof(blockingQueue));
      blockingQueue_init(the_scheduler->readyPriorityQueue[i]);
    }
    the_scheduler->nextProcess = &nextPriorityProcess;
    the_scheduler->readyProcess = &readyPriorityProcess;
    break;
  case LOTTERY:
    the_scheduler->readyArrayQueue = (blockedList*)malloc(sizeof(blockedList));
    blockedList_init(the_scheduler->readyArrayQueue, 4);
    the_scheduler->nextProcess = &nextLotteryProcess;
    the_scheduler->readyProcess = &readyLotteryProcess;
    break;
  }
  return rv;
}

void printList(arrayList* the_list){
  int i = 0;
  char* proc_type;
  printf("\nArrayList: head->");
  for(i = 0; i < the_list -> size; i++){
    proc_type = processTypeToString(((Process*)list_get(the_list, i))->t);
    printf("[%s]%s",proc_type, i == (the_list->size - 1) ? "":"," );
  }
  printf("<-tail");
}

